import threading
import yaml
import json
import datetime
import pathlib
import time
import shutil
import subprocess
import os
import sys
import re
import requests
import psutil
import ctypes
import typing
import configparser
import zipfile
import sqlite3
import random
import logging
import cv2
import base64
import io
import PIL.Image
import uiautomation
import rich


from selenium.webdriver.edge.webdriver import WebDriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import *


from PySide6.QtWidgets import (QWidget, QMainWindow, QDialog, QFileDialog, QMessageBox, QPushButton, QListView, QListWidget,
                               QListWidgetItem, QLineEdit, QTextEdit, QLabel, QHeaderView, QInputDialog, QApplication, QProgressBar, QComboBox, QTableWidget, QTableWidgetItem, QTableView,
                               QHBoxLayout, QVBoxLayout, QLayout)

from PySide6.QtCore import (Qt, QCoreApplication, QObject, QMetaObject,
                            Signal, Slot,
                            QDateTime,
                            QThread, QThreadPool, QTimer, QRunnable,
                            QTemporaryFile, QTemporaryDir,
                            QJsonArray, QJsonDocument, QJsonParseError, QJsonValue,
                            QFile, QFileDevice, QIODevice, QIODeviceBase, QFileInfo,
                            QTextStream, QStringListModel, QModelIndex)


def print_with_time(*args, **kwargs) -> None:
    """带时间戳的控制台日志输出"""
    current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    rich.print(f"[bold blue][{current_time}][/bold blue]", *args, **kwargs)


def Checkout_Debug_Mode_Activate() -> bool:
    """检查Edge调试模式是否可用"""
    url = "http://127.0.0.1:9652/json/version"
    try:
        response = requests.get(url=url)
        result = response.status_code == 200
    except Exception as e:
        print_with_time(f"请求调试模式失败: {e}")
        result = False
    return result


def Fast_Create_WebDriver_Wait_Action() -> WebDriver:
    """创建连接调试模式Edge的WebDriver实例"""
    options = Options()
    options.add_experimental_option("debuggerAddress", "127.0.0.1:9652")
    service = Service()
    driver = WebDriver(options=options, service=service)
    return driver


def Goto_CurrentActivateUrl(driver: WebDriver) -> None:
    """切换到当前活跃的浏览器标签页"""
    try:
        debug_json_data = requests.get(url="http://127.0.0.1:9652/json", timeout=5).json()
    except Exception as e:
        print_with_time(f"获取标签页信息失败: {e}")
        return

    if not isinstance(debug_json_data, list):
        print_with_time("标签页数据格式错误（非列表）")
        return

    # 过滤有效标签页
    valid_tabs = list(
        filter(lambda item: item['type'] == "page" and not (
            item['title'].lower().startswith(("devtools", "offscreen")) or
            item['url'].lower().startswith(("chrome-extension", "chrome-devtools"))
        ), debug_json_data)
    )
    if valid_tabs:
        driver.switch_to.window(valid_tabs[0]['id'])


def Checkout_Has_Msedge_Process(_process_name: str = "msedge.exe") -> bool:
    """检查Edge进程是否运行"""
    return any(_proc.info['name'] == _process_name for _proc in psutil.process_iter(['name']))


def Close_All_Msedge_Process(_process_name: str = "msedge.exe") -> bool:
    """关闭所有Edge进程"""
    success = True
    for proc in psutil.process_iter(['pid', 'name']):
        if proc.info['name'] == _process_name:
            try:
                proc.kill()
                print_with_time(f"关闭Edge进程（PID: {proc.info['pid']}）")
            except Exception as e:
                print_with_time(f"关闭进程失败（PID: {proc.info['pid']}）: {e}")
                success = False
    return success


def Start_Debugmode_Browser(_edge_path: str = "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe", _port: int = 9652):
    """启动调试模式Edge浏览器"""
    command = rf'"{_edge_path}" --remote-debugging-port={_port} --disable-extensions "http://127.0.0.1:9652/json/version"'
    CREATE_NEW_CONSOLE = 0x00000010
    subprocess.Popen(command, shell=True, creationflags=CREATE_NEW_CONSOLE)


def Custom_Message(parent_hwnd: int = 0, title: str = '提示', text: str = 'default_text', type: int = 0) -> int:
    """调用Windows系统消息框"""
    return ctypes.windll.user32.MessageBoxW(parent_hwnd, text, title, type)


# -------------------------- 核心功能：提取URL并去重保存 --------------------------
def save_unique_tab_urls(driver: WebDriver, save_file: str = "save_url.txt") -> None:
    """
    遍历所有标签页，提取URL并去重保存（仅追加文件中不存在的URL）
    
    Args:
        driver (WebDriver): Selenium WebDriver实例
        save_file (str): 保存URL的文件名
    """
    # 1. 获取所有标签页句柄
    all_handles = driver.window_handles
    if not all_handles:
        print_with_time(f"[yellow]未检测到任何标签页[/yellow]")
        return

    print_with_time(f"发现 {len(all_handles)} 个标签页，开始提取并去重URL...")
    original_handle = driver.current_window_handle  # 记录原始标签页
    new_urls = []  # 存储本次提取的有效URL

    # 2. 遍历标签页提取有效URL
    for handle in all_handles:
        try:
            driver.switch_to.window(handle)
            current_url = driver.current_url.strip()  # 去除首尾空白

            # 过滤无效URL
            if current_url.lower().startswith(("chrome-extension://", "about:blank", "chrome-devtools://")):
                continue

            new_urls.append(current_url)
        except NoSuchWindowException:
            print_with_time(f"[yellow]跳过已关闭的标签页[/yellow]")
        except Exception as e:
            print_with_time(f"[yellow]处理标签页出错: {str(e)}[/yellow]")

    # 3. 恢复到原始标签页
    driver.switch_to.window(original_handle)

    # 4. 读取已有URL（用于去重）
    existing_urls = set()  # 用集合存储已有URL，自动去重且查询更快
    if os.path.exists(save_file):
        try:
            with open(save_file, 'r', encoding='utf-8') as f:
                # 读取所有行，去除空白和换行符，过滤空行
                existing_urls = {line.strip() for line in f if line.strip()}
            print_with_time(f"已读取文件中 {len(existing_urls)} 个不重复URL（用于去重）")
        except Exception as e:
            print_with_time(f"[yellow]读取已有URL失败（将忽略历史记录直接保存）: {str(e)}[/yellow]")
            existing_urls = set()

    # 5. 筛选出文件中不存在的新URL
    unique_new_urls = [url for url in new_urls if url not in existing_urls]

    # 6. 追加保存新URL（每行一个，无多余文本）
    if unique_new_urls:
        with open(save_file, 'a+', encoding='utf-8') as f:
            for url in unique_new_urls:
                f.write(f"{url}\n")
        
        # 控制台输出结果
        file_abs_path = os.path.abspath(save_file)
        print_with_time(f"[green]成功添加 {len(unique_new_urls)} 个新URL（已去重）[/green]")
        print_with_time(f"保存路径：{file_abs_path}")
        print_with_time(f"当前文件总不重复URL数量：{len(existing_urls) + len(unique_new_urls)}")
    else:
        print_with_time(f"[yellow]未发现新URL（所有URL已存在于文件中）[/yellow]")


# -------------------------- 主程序入口 --------------------------
if __name__ == "__main__":
    app = QApplication(sys.argv)
    print_with_time(f"程序启动：开始处理Edge标签页URL提取（含去重功能）")

    # 1. 检查并启动Edge调试模式
    if not Checkout_Has_Msedge_Process():
        print_with_time("未检测到Edge进程，启动调试模式浏览器...")
        Start_Debugmode_Browser()
        time.sleep(3)
        if not Checkout_Has_Msedge_Process():
            Custom_Message(title="错误", text="Edge启动失败，请手动启动", type=0)
            sys.exit(1)

    # 2. 检查调试模式是否正常
    if not Checkout_Debug_Mode_Activate():
        _select = Custom_Message(
            text="Edge未处于调试模式！\n是否关闭所有Edge进程并重启（调试模式）？",
            title="调试模式检测",
            type=1  # 确定(1) + 取消(2)
        )
        if _select == 1:
            Close_All_Msedge_Process()
            time.sleep(2)
            Start_Debugmode_Browser()
            time.sleep(3)
            if not Checkout_Debug_Mode_Activate():
                Custom_Message(title="错误", text="调试模式启动失败", type=0)
                sys.exit(1)
        else:
            print_with_time("用户取消操作，程序退出")
            sys.exit(0)

    # 3. 初始化WebDriver并连接浏览器
    try:
        driver = Fast_Create_WebDriver_Wait_Action()
        Goto_CurrentActivateUrl(driver)
        print_with_time(f"已连接浏览器，当前标签页：{driver.current_url}")
    except Exception as e:
        print_with_time(f"[red]连接浏览器失败: {str(e)}[/red]")
        Custom_Message(title="错误", text=f"连接失败：{str(e)}", type=0)
        sys.exit(1)

    # 4. 核心任务：提取并去重保存URL
    save_unique_tab_urls(driver=driver, save_file="save_url.txt")

    # 5. 任务完成
    print_with_time(f"\n[bold green]URL提取与去重任务完成[/bold green]")
    # driver.quit()  # 如需关闭浏览器可取消注释

    sys.exit(app.exec())